package dgraph

import (
	"context"
	"fmt"

	dgo "github.com/dgraph-io/dgo/v200"
	"github.com/dgraph-io/dgo/v200/protos/api"
	"google.golang.org/grpc"
)

type DgraphClients struct {
	Clients map[string]*dgo.Dgraph
}

func NewDgraphClients() *DgraphClients {
	return &DgraphClients{Clients: make(map[string]*dgo.Dgraph)}
}

func (cs *DgraphClients) AddDgraphClient(name, host string, port int) error {
	if cs.CheckClientIsExisted(name) {
		return fmt.Errorf("current client: %s is already existed\n", name)
	}

	connStr := fmt.Sprintf("%s:%d", host, port)
	conn, err := grpc.Dial(connStr, grpc.WithInsecure())
	if err != nil {
		return err
	}

	client := dgo.NewDgraphClient(api.NewDgraphClient(conn))
	cs.Clients[name] = client
	return nil
}

func (cs *DgraphClients) CheckClientIsExisted(clientName string) bool {
	_, ok := cs.Clients[clientName]
	return ok
}

func (cs *DgraphClients) DropAll(clientName string) error {
	if !cs.CheckClientIsExisted(clientName) {
		return fmt.Errorf("Dgraph Client: %s is not existed\n", clientName)
	}

	err := cs.Clients[clientName].Alter(context.Background(), &api.Operation{DropOp: api.Operation_ALL})
	return err
}

func (cs *DgraphClients) DropData(clientName string) error {
	if !cs.CheckClientIsExisted(clientName) {
		return fmt.Errorf("Dgraph Client: %s is not existed\n", clientName)
	}

	err := cs.Clients[clientName].Alter(context.Background(), &api.Operation{DropOp: api.Operation_DATA})

	return err
}

func (cs *DgraphClients) SetSchema(clientName string, schema string) error {
	if !cs.CheckClientIsExisted(clientName) {
		return fmt.Errorf("Dgraph Client: %s is not existed\n", clientName)
	}

	err := cs.Clients[clientName].Alter(context.Background(), &api.Operation{Schema: schema})
	return err
}

func (cs *DgraphClients) SetJsonData(clientName string, data []byte) error {
	if !cs.CheckClientIsExisted(clientName) {
		return fmt.Errorf("Dgraph Client: %s is not existed\n", clientName)
	}

	mu := &api.Mutation{CommitNow: true}
	mu.SetJson = data
	_, err := cs.Clients[clientName].NewTxn().Mutate(context.Background(), mu)
	return err
}

func (cs *DgraphClients) SetNquadsData(clientName string, data []byte) error {
	if !cs.CheckClientIsExisted(clientName) {
		return fmt.Errorf("Dgraph Client: %s is not existed\n", clientName)
	}

	mu := &api.Mutation{CommitNow: true}
	mu.SetNquads = data
	_, err := cs.Clients[clientName].NewTxn().Mutate(context.Background(), mu)
	return err
}

func (cs *DgraphClients) QueryDataWithVars(clientName string, query string, vars map[string]string) ([]byte, error) {
	if !cs.CheckClientIsExisted(clientName) {
		return nil, fmt.Errorf("Dgraph Client: %s is not existed\n", clientName)
	}

	res, err := cs.Clients[clientName].NewTxn().QueryWithVars(context.Background(), query, vars)
	if err != nil {
		return nil, err
	}
	return res.Json, err
}

func (cs *DgraphClients) QueryData(clientName string, query string) ([]byte, error) {
	if !cs.CheckClientIsExisted(clientName) {
		return nil, fmt.Errorf("Dgraph Client: %s is not existed\n", clientName)
	}

	res, err := cs.Clients[clientName].NewTxn().Query(context.Background(), query)
	return res.Json, err
}

func (cs *DgraphClients) DeleteJsonData(clientName string, data []byte) error {
	if !cs.CheckClientIsExisted(clientName) {
		return fmt.Errorf("Dgraph Client: %s is not existed\n", clientName)
	}

	mu := &api.Mutation{CommitNow: true}
	mu.DeleteJson = data
	_, err := cs.Clients[clientName].NewTxn().Mutate(context.Background(), mu)
	return err
}

func (cs *DgraphClients) DeleteNquadsData(clientName string, data []byte) error {
	if !cs.CheckClientIsExisted(clientName) {
		return fmt.Errorf("Dgraph Client: %s is not existed\n", clientName)
	}

	mu := &api.Mutation{CommitNow: true}
	mu.DelNquads = data
	_, err := cs.Clients[clientName].NewTxn().Mutate(context.Background(), mu)
	return err
}
